﻿using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using System.Xml.Linq;

namespace DotnetFramework.CAP.IocRefction
{
    internal class Container : IContainer
    {
        ContainerBuilder _builder;
        public Container(ContainerBuilder builder)
        {
            _builder = builder;
        }
        #region resolve begin
        Type TryLoadTwice(string typename, XmlNode componet, Assembly defaultAssembly)
        {
            try
            {
                return LoadType(typename, defaultAssembly);
            }
            catch
            {
                if (CurrentDomain_AssemblyResolve(componet,
                    new ResolveEventArgs(typename)) != null)
                {
                    return LoadType(typename, defaultAssembly);
                }
            }
            return null;
        }
        private System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
        {
            try
            {
                //logger.Trace("try AssemblyResolve {0}", args?.Name);
                var items = args.Name.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
                foreach (var item in items)
                {
                    var path = item.Trim() + ".dll";
                    if (File.Exists(path))
                    {
                        return System.Reflection.Assembly.LoadFrom(path);
                    }
                }
            }
            catch (Exception ex)
            {
                //logger.Trace("try AssemblyResolve {0} error:{1}", args?.Name, ex.ToString());
            }
            //logger.Trace("load or unload error:{0}", args?.Name);
            //fix load failed dll
            return null;
        }
        protected virtual Type LoadType(string typeName, Assembly defaultAssembly)
        {
            if (typeName == null)
            {
                throw new ArgumentNullException("typeName");
            }
            if (typeName.Length == 0)
            {
                throw new ArgumentException("typeName error");
            }
            var type = Type.GetType(typeName);

            if (type == null && defaultAssembly != null)
            {
                type = defaultAssembly.GetType(typeName, false); // Don't throw on error; we'll check it later.
            }
            if (type == null)
            {
                throw new Exception(typeName + " type not found");
            }
            return type;
        }
        object _lockSingleInstance = new object();
        public T Resolve<T>()
        {
            var data = default(T);
            try
            {
                var name = typeof(T).FullName;
                var r = Resolve(typeof(T));
                if (r != null)
                    return (T)r;
            }
            catch (Exception ex)
            {
                //logger.Trace($"t:{typeof(T).FullName} ResolveReflection failed:{ex.ToString()}");
            }
            return data;
        }

        public object Resolve(Type serviceType)
        {
            var name = serviceType.FullName;
            return ResolveNamed(name, serviceType);
        }
        public object ResolveNamed(string name, Type serviceType)
        {
            object data = null;
            if (typeof(IEnumerable).IsAssignableFrom(serviceType) && serviceType.GetGenericArguments().Length > 0)
            {
                data = ResolveEnumerable(serviceType);
            }
            else
            {
                if (_identityTypes.ContainsKey(name))
                {
                    var bag = _identityTypes[name];
                    if (!_identityTypes[name].IsSingleInstance)
                    {
                        data = CreateInstance(bag.Callback, bag.InstanceType);
                    }
                    else
                    {
                        AddSingleInstance(name, () => CreateInstance(bag.Callback, bag.InstanceType));
                        return _identityTypes[name].Instance;
                    }
                }
            }
            return data;
        }
        public object ResolveEnumerable(Type type)
        {
            var metaType = type.GetGenericArguments()[0];
            // 先创建开放泛型
            Type openType = typeof(List<>);
            // 再创建具象泛型
            Type target = openType.MakeGenericType(new[] { metaType });
            var t = Activator.CreateInstance(target);

            if (_identityTypes.ContainsKey(metaType.FullName))
            {
                foreach (Type item in ((ListCommponetBag)_identityTypes[metaType.FullName]).InstanceTypes)
                {
                    var p = CreateInstanceWithDepends(item);                    
                    //var p = Resolve(metaType.FullName, item);
                    target.InvokeMember("Add", BindingFlags.Instance | BindingFlags.Public | BindingFlags.InvokeMethod, null, t, new object[] { p });
                }
            }
            AddSingleInstance(type.FullName, () => t);
            return t;
        }
        void AddSingleInstance(string name, Func<object> func)
        {
            if (_identityTypes.ContainsKey(name) && _identityTypes[name].IsSingleInstance)
            {
                if (_identityTypes[name].Instance == null)
                {
                    lock (_lockSingleInstance)
                    {
                        if (_identityTypes[name].Instance == null)
                        {
                            _identityTypes[name].Instance = func();
                            if (typeof(DotNetFramework.CAP.Internal.MethodMatcherCache) == _identityTypes[name].Instance.GetType())
                            {

                            }
                        }
                    }
                }
            }
        }
        object CreateInstance(Func<object, object> creator, Type type)
        {
            if (creator != null)
            {
                return creator(this);
            }
            else
                return CreateInstanceWithDepends(type);
        }
        object CreateInstanceWithDepends(Type type)
        {
            var constructors = type.GetConstructors();


            if (constructors.Length == 0)//接口
            {
                return Resolve(type);
            }
            if (_identityTypes.ContainsKey(type.FullName))
            {
                if (_identityTypes[type.FullName].Instance != null)
                    return _identityTypes[type.FullName].Instance;
                else if (_identityTypes[type.FullName].Callback != null)
                    return _identityTypes[type.FullName].Callback(this);
            }

            object[] parms = null;
            foreach (var constructor in constructors)
            {
                var parmInfos = constructor.GetParameters();

                parms = new object[parmInfos.Length];
                for (int i = 0; i < parmInfos.Length; i++)
                {
                    parms[i] = CreateInstanceWithDepends(parmInfos[i].ParameterType);
                    AddSingleInstance(parmInfos[i].ParameterType.FullName, () => parms[i]);
                }
                break;
            }
            if (parms != null && parms.Length > 0)
                return Activator.CreateInstance(type, parms);
            else
                return Activator.CreateInstance(type);
        }

        #endregion resolve end
        #region Register begin
        public ContainerBuilder Register<T, T2>(Func<object, object> func)
        {
            var type = typeof(T);
            var serverType = typeof(T2);
            Resiger2(null, null, true, type, serverType, func);
            return _builder;
        }
        public ContainerBuilder RegisterInstance<T, T2>(T instance)
        {
            var type = typeof(T);
            var serverType = typeof(T2);

            Resiger<T, T2>(instance, null, true);
            return _builder;
        }
        public ContainerBuilder RegisterGeneric(Type type, Type serverType, bool isSingle = false)
        {
            Resiger2(null, null, isSingle, type, serverType);
            return _builder;
        }
        public ContainerBuilder RegisterGenericNamed(Type type, Type serverType,string name, bool isSingle = false)
        {
            Resiger2(null, name, isSingle, type, serverType);
            return _builder;
        }
        public ContainerBuilder RegisterType<T, T2>(bool isSingle = false)
        {
            var type = typeof(T);
            var serverType = typeof(T2);

            Resiger<T, T2>(default(T), null, isSingle);
            return _builder;
        }
        public ContainerBuilder RegisterTypeNamed<T, T2>(string typeName, bool isSingle = false)
        {
            var type = typeof(T);
            var serverType = typeof(T2);

            Resiger<T, T2>(default(T), typeName, isSingle);
            return _builder;
        }
        public ContainerBuilder TryAddEnumerable(Type type, Type serverType)
        {
            var interfaceType = serverType.FullName;
            if (!_identityTypes.ContainsKey(interfaceType))
                _identityTypes[interfaceType] = new ListCommponetBag();
            ((ListCommponetBag)_identityTypes[interfaceType]).InstanceTypes.Add(type);
            return _builder;
        }
        void Resiger<T, T2>(T instance, string keyName, bool isSingleInstance)
        {
            var type = typeof(T);
            var interfaceType = typeof(T2);

            Resiger2(instance, keyName, isSingleInstance, type, interfaceType);
        }
        void Resiger2(object instance, string keyName, bool isSingleInstance, Type type, Type interfaceType, Func<object, object> func = null)
        {
            var name = interfaceType.FullName;
            if (!string.IsNullOrEmpty(keyName))
                name = keyName;
            _identityTypes[name] = new SingleCommponetBag()
            {
                InstanceType = type,
                Instance = instance,
                IsSingleInstance = isSingleInstance,
                Service = interfaceType,
            };
            if (!string.IsNullOrEmpty(keyName))
            {
                _nameTypes[keyName] = new CommponetBag() { InstanceType = type, Service = interfaceType, Name = keyName };
            }
        }

        ConcurrentDictionary<string, SingleCommponetBag> _identityTypes = new ConcurrentDictionary<string, SingleCommponetBag>();
        ConcurrentDictionary<string, CommponetBag> _nameTypes = new ConcurrentDictionary<string, CommponetBag>();
        public class CommponetBag
        {
            public Type Service { get; set; }
            public Type InstanceType { get; set; }
            public string Name { get; set; }
            //public Assembly InstanceAssembly { get; set; }
        }
        class SingleCommponetBag : CommponetBag
        {
            public object Instance { get; set; }
            public bool IsSingleInstance { get; set; } = true;
            public Func<object, object> Callback { get; set; }
        }
        class ListCommponetBag : SingleCommponetBag
        {
            public List<Type> InstanceTypes { get; set; } = new List<Type>();
        }
        #endregion Register end
    }
}
